import hashlib
from django.db import models
from django.contrib.auth.models import Permission, AbstractBaseUser, PermissionsMixin
from django.contrib.auth.base_user import BaseUserManager

from load_planner.models import ShippingContainer, Task
from .utils.db import is_valid_pgsql_schema_name, get_model_permissions


class Tenant(models.Model):

    schema_name = models.CharField(
        max_length=75,
        unique=True,
        help_text='该租户的独占数据库Schema名称',
        validators=[is_valid_pgsql_schema_name])

    organization = models.TextField(
        null=True,
        default=None,
        help_text='租户的企业组织名称(可选)')

    created = models.DateTimeField(
        auto_now_add=True,
        help_text='数据创建时间')

    def __str__(self) -> str:
        return self.schema_name


class RoleManager(models.Manager):

    def create_admin_role(self, tenant):
        permissions = Permission.objects.all()
        instance, _ = self.get_or_create('admin', tenant, permissions)
        return instance

    def create_basic_role(self, tenant):
        # A basic role can only
        # - View: User, Container
        # - Update: User
        # - Create/Update/Delete: Task
        permissions = get_model_permissions(
            models=[User, ShippingContainer],
            permission_type='read'
        ) + get_model_permissions(
            models=[User],
            permission_type='change'
        ) + get_model_permissions(
            models=[Task],
            permission_type='all'
        )

        instance, _ = self.get_or_create('basic', tenant, permissions=permissions)
        return instance

    def get_or_create(self, name, tenant, permissions=None):
        role, created = super().get_or_create(name=name, tenant=tenant)

        if not created:
            return role, created

        if permissions:
            role.permissions.set(permissions)
        return role, created


class Role(models.Model):
    name = models.CharField(
        'name',
        max_length=150,
        help_text='角色名称，如"管理员"')

    tenant = models.ForeignKey(
        Tenant,
        related_name='roles',
        on_delete=models.CASCADE,
        help_text='该角色的所属租户')

    permissions = models.ManyToManyField(
        Permission,
        help_text='该角色拥有的权限集',
        blank=True)

    created = models.DateTimeField(
        auto_now_add=True,
        help_text='数据创建时间')

    objects = RoleManager()

    class Meta:
        unique_together = [['name', 'tenant']]

    def __str__(self) -> str:
        return self.name


class UserManager(BaseUserManager):

    def create(self, roles=None, **user_attrs):
        username = User.make_default_username(user_attrs['email'])
        instance = self.model(username=username, **user_attrs)
        instance.set_password(user_attrs['password'])
        instance.save()

        if roles:
            instance.roles.set(roles)

        return instance


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField()

    username = models.TextField(
        unique=True,
        help_text='自动生成的系统用户名，格式为 "<邮箱>_<租户Schema>" ')

    first_name = models.CharField(max_length=32, default='', help_text='姓')

    last_name = models.CharField(max_length=32, default='', help_text='名')

    roles = models.ManyToManyField(
        Role,
        blank=True,
        help_text='该用户的角色集',
        related_name="user_set",
        related_query_name="user")

    tenant = models.ForeignKey(
        Tenant,
        related_name='users',
        on_delete=models.SET_NULL,
        help_text='该用户所属租户',
        null=True)

    created = models.DateTimeField(
        auto_now_add=True,
        help_text='数据创建时间')

    USERNAME_FIELD = 'username'

    objects = UserManager()

    class Meta:
        unique_together = [['email', 'tenant']]
    
    @property
    def is_admin(self):
        return self.roles.filter(name='admin').exists()

    @staticmethod
    def make_default_tenant_schema(email: str) -> str:
        email = email.lower().strip()
        DIGEST_SIZE = 8
        email_hash = hashlib.blake2b(bytes(email, 'ascii'), digest_size=DIGEST_SIZE).hexdigest()
        return f'zmm_{email_hash}'

    @staticmethod
    def make_default_username(email: str) -> str:
        if not email:
            return None

        schema_name = User.make_default_tenant_schema(email)
        return f'{email}_{schema_name}'
